package cn.com.cpic.insurance.rulecheck.core.engine;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import cn.com.cpic.insurance.rulecheck.core.ModifiableSet;
import cn.com.cpic.insurance.rulecheck.core.ReferModifiableSet;
import cn.com.cpic.insurance.rulecheck.core.condition.Condition;
import cn.com.cpic.insurance.rulecheck.core.condition.ConditionGroup;
import cn.com.cpic.insurance.rulecheck.core.condition.ConditionSwitch;
import cn.com.cpic.insurance.rulecheck.core.condition.ExpressionCondition;
import cn.com.cpic.insurance.rulecheck.core.condition.SimpleConditionGroup;
import cn.com.cpic.insurance.rulecheck.core.condition.SimpleConditionSwitch;
import cn.com.cpic.insurance.rulecheck.core.rule.ExpressionRuleConsumer;
import cn.com.cpic.insurance.rulecheck.core.rule.Rule;
import cn.com.cpic.insurance.rulecheck.core.rule.RuleConsumer;
import cn.com.cpic.insurance.rulecheck.core.rule.RuleGroup;
import cn.com.cpic.insurance.rulecheck.core.rule.SimpleRule;
import cn.com.cpic.insurance.rulecheck.core.rule.SimpleRuleGroup;

/**
 * 
 * @author 代波
 * @since 2011-8-24
 */
public abstract class Engine {
	private static EngineContext engineContext = null;

	private Engine() {
		// 禁止实例化
	}

	public static EngineContext getEngineContext() {
		return engineContext;
	}

	public static synchronized void init() {
		engineContext = new SimpleEngineContext();
	}

	public static synchronized void init(InputStream in) throws IOException, ClassNotFoundException {
		ObjectInputStream oin;
		if (ObjectInputStream.class.isInstance(in)) {
			oin = (ObjectInputStream) in;
		} else {
			oin = new ObjectInputStream(in);
		}
		try {
			engineContext = (EngineContext) oin.readObject();
		} finally {
			oin.close();
		}
	}

	public static void checkInited() {
		if (engineContext == null)
			throw new NullPointerException("EngineContext未初始化");
	}

	public static synchronized void save(OutputStream out) throws IOException {
		checkInited();

		ObjectOutputStream oout;
		if (ObjectOutputStream.class.isInstance(out)) {
			oout = (ObjectOutputStream) out;
		} else {
			oout = new ObjectOutputStream(out);
		}
		try {
			oout.writeObject(engineContext);
		} finally {
			oout.close();
		}
	}

	private static ModifiableSet<Condition> newReferConditionSet(Condition... conditions) {
		return new ReferModifiableSet<String, Condition>(engineContext.getConditions().getConditionSet(), conditions);
	}

	private static ModifiableSet<RuleConsumer<?>> newReferRuleConsumerSet(RuleConsumer<?>... consumers) {
		return new ReferModifiableSet<String, RuleConsumer<?>>(engineContext.getRules().getRuleConsumerSet(), consumers);
	}

	private static ModifiableSet<Rule> newReferRuleSet(Rule... rules) {
		return new ReferModifiableSet<String, Rule>(engineContext.getRules().getRuleSet(), rules);
	}

	public static ConditionGroup createConditionGroup(String token, Condition... conditions) {
		checkInited();
		return new SimpleConditionGroup(token, newReferConditionSet(conditions));
	}

	public static ConditionSwitch addConditionSwitch(String token, Condition... conditions) {
		checkInited();
		ConditionSwitch cs = new SimpleConditionSwitch(token, newReferConditionSet(conditions));
		return engineContext.getConditions().getConditionSwitchSet().add(cs) ? cs : null;
	}

	public static Condition addExpressionCondition(String token, String expression) {
		checkInited();
		Condition c = new ExpressionCondition(token, expression);
		return engineContext.getConditions().getConditionSet().add(c) ? c : null;
	}

	public static ExpressionRuleConsumer addExpressionRuleConsumer(String token, String expression, String... tags) {
		checkInited();
		ExpressionRuleConsumer c = new ExpressionRuleConsumer(token, expression, tags);
		return engineContext.getRules().getRuleConsumerSet().add(c) ? c : null;
	}

	public static Rule addRule(String token, String bom, String desc, Object value, RuleConsumer<?>... consumers) {
		checkInited();
		Rule rule = new SimpleRule(token, bom, desc, value, newReferRuleConsumerSet(consumers));
		return engineContext.getRules().getRuleSet().add(rule) ? rule : null;
	}

	public static RuleGroup addRuleGroup(String token, String name, String desc, Rule... rules) {
		checkInited();
		RuleGroup rg = new SimpleRuleGroup(token, name, desc, newReferRuleSet(rules));
		return engineContext.getRules().getRuleGroupSet().add(rg) ? rg : null;
	}
}
